package com.googlecode.perfectable.visuable.configuration.guice;

import static com.google.common.base.Preconditions.checkState;

import java.util.List;

import javax.annotation.Nullable;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.google.common.base.CharMatcher;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.googlecode.perfectable.introspection.Classes;

public class GuiceInjectorListener implements ServletContextListener {

	private static final Splitter MODULE_SPLITTER = Splitter.on(CharMatcher.BREAKING_WHITESPACE)
			.omitEmptyStrings().trimResults();

	private static final String MODULES_ATTRIBUTE_NAME = "guiceModules";
	private static Optional<Injector> injector = Optional.absent(); // MARK make this instance member, not class

	@Override
	public void contextInitialized(@Nullable ServletContextEvent sce) {
		if(sce == null) {
			throw new IllegalArgumentException();
		}
		checkState(!GuiceInjectorListener.injector.isPresent());
		final ServletContext servletContext = sce.getServletContext();
		String modulesString = servletContext.getInitParameter(MODULES_ATTRIBUTE_NAME);
		if(modulesString == null) {
			throw new IllegalStateException("Context attribute " + MODULES_ATTRIBUTE_NAME
					+ " must exist and list guice modules");
		}
		List<Module> modules = createModules(modulesString);
		Injector newInjector = Guice.createInjector(modules);
		GuiceInjectorListener.injector = Optional.of(newInjector);
	}

	@Override
	public void contextDestroyed(@Nullable ServletContextEvent sce) {
		if(sce == null) {
			throw new IllegalArgumentException();
		}
		checkState(GuiceInjectorListener.injector.isPresent());
		GuiceInjectorListener.injector = Optional.absent();
	}

	private static List<Module> createModules(String modulesString) {
		Iterable<String> moduleClasses = MODULE_SPLITTER.split(modulesString);
		List<Module> result = Lists.newLinkedList();
		for(String moduleClass : moduleClasses) {
			Module module = Classes.instantiate(moduleClass, Module.class);
			result.add(module);
		}
		return result;
	}

	public static Injector injector() {
		return GuiceInjectorListener.injector.get();
	}

}
